/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package desarrollosoft.ghce.dao.implementaciones;

import desarrollosoft.ghce.dao.interfaces.IMonodrogaDAO;
import desarrollosoft.ghce.entidades.Elemento;
import desarrollosoft.ghce.entidades.Monodroga;
import desarrollosoft.ghce.entidades.Sexo;
import desarrollosoft.ghce.entidades.Tratamiento;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author ariel
 */
@Repository("monodrogaDAO")
public class MonodrogaDAO implements IMonodrogaDAO {

    @PersistenceContext
    EntityManager entityManager;
    
    @Override
    public List<Monodroga> buscarMonodroga(String nombre) {
        Query query = entityManager.createQuery("SELECT m FROM Monodroga m WHERE "
                + "m.nombre LIKE CONCAT ('%',:nombre,'%')"
                + " ORDER BY m.nombre ASC");
        query.setParameter("nombre", nombre);
        query.setMaxResults(50);
        return query.getResultList();
    }

    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void eliminarMonodroga(Monodroga medicamento) {
        entityManager.remove( entityManager.merge(medicamento) );
    }

    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void modificarMonodroga(Monodroga medicamento) {
        entityManager.merge(medicamento);
    }

    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void crearMonodroga(Monodroga medicamento) {
        entityManager.persist(medicamento);
    }

    @Override
    public boolean existeMonodrogaConNombre(String nombreMayusculas) {
        Query consulta = entityManager.createQuery(
                "SELECT m FROM Monodroga m WHERE UPPER(m.nombre) = :nombreMayusculas"
        );
        consulta.setParameter("nombreMayusculas", nombreMayusculas);
        List<Monodroga> lista = consulta.getResultList();
        if (lista.isEmpty()) {
            return false;
        }
        return true;
    }

    @Override
    public boolean existeMonodrogaConNombreYdistintoID(Long id, String nombreMayusculas) {
        Query consulta = entityManager.createQuery(
                "SELECT m FROM Monodroga m WHERE UPPER(m.nombre) = :nombreMayusculas "
                        + "AND m.id != :id"
        );
        consulta.setParameter("nombreMayusculas", nombreMayusculas);
        consulta.setParameter("id", id);
        List<Monodroga> lista = consulta.getResultList();
        if (lista.isEmpty()) {
            return false;
        }
        return true;
    }

    @Override
    public List<Monodroga> obtenerTodosMonodrogas() {
        Query query = entityManager.createQuery("SELECT m FROM Monodroga m "
                + "ORDER BY m.nombre ASC");
        query.setMaxResults(50);
        return query.getResultList();
    }

    @Override
    public List<Monodroga> obtenerMonodrogas(
            Sexo sexo, 
            int edadDesde, int edadHasta, 
            int anioDesde, int anioHasta, 
            int mesDesde, int mesHasta) {
        
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        
        calendar.set(Calendar.YEAR, anioDesde);
        calendar.set(Calendar.MONTH, mesDesde);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getMinimum(Calendar.DAY_OF_MONTH));
        
        Date fechaMinima = calendar.getTime();
        
        calendar.set(Calendar.YEAR, anioHasta);
        calendar.set(Calendar.MONTH, mesHasta);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getMaximum(Calendar.DAY_OF_MONTH));
           
        Date fechaMaxima = calendar.getTime();
        
        String jpql = "SELECT e FROM Elemento e JOIN e.paciente WHERE TYPE(e) IN :clases AND "
                + "e.fecha BETWEEN :fechaMinima AND :fechaMaxima";
        
        if (sexo != null) {
            jpql += " AND e.paciente.sexo = :sexo";
        }
        
        Query consultaGrande = entityManager.createQuery(jpql);
        
        consultaGrande.setParameter("clases",Arrays.asList(Tratamiento.class));
        consultaGrande.setParameter("fechaMinima", fechaMinima);
        consultaGrande.setParameter("fechaMaxima", fechaMaxima);
        
        if (sexo != null) {
            consultaGrande.setParameter("sexo", sexo);
        }
        
        consultaGrande.setMaxResults(20); // 20 ??
        
        List<Elemento> listaElementos = consultaGrande.getResultList();
        // ARMO NUEVA LISTA CALCULANDO EDAD Y GUARDANDO LOS QUE CAIGAN EN RANGO DE EDAD
        List<Elemento> listaFiltrada = filtrarLosQuePertenezcanAlRango(listaElementos, edadDesde, edadHasta);
        
        ContadorMedicamentos contadorMedicamentos = new ContadorMedicamentos();
        for (Elemento elemento : listaFiltrada) {
            Tratamiento tratamiento = (Tratamiento) elemento;
            Monodroga medicamento = tratamiento.getMedicamento();
            contadorMedicamentos.aparecioMedicamento(medicamento);
        }
        return contadorMedicamentos.getListaOrdenadaMayorAmenor();
    }
    
    private List<Elemento> filtrarLosQuePertenezcanAlRango(List<Elemento> listaElementos, int edadDesde, int edadHasta) {
        List<Elemento> listaDevolver = new ArrayList<>();
        
        for (Elemento elemento : listaElementos) {
            int edadEnElMomento = calcularEdadEnMomento(elemento);
            if (edadDesde <= edadEnElMomento && edadEnElMomento <= edadHasta) {
                listaDevolver.add(elemento);
            }
        }
        return listaDevolver;
    }
    
    private int calcularEdadEnMomento(Elemento elemento) {
        Date fechaSuceso = elemento.getFecha();
        Date fechaNacimiento = elemento.getPaciente().getFechaNacimiento();
        
        GregorianCalendar fechaSucesoCal = new GregorianCalendar();
        fechaSucesoCal.setTime(fechaSuceso);
        GregorianCalendar fechaNacimientoCal = new GregorianCalendar();
        fechaNacimientoCal.setTime(fechaNacimiento);
        
        int anio = fechaSucesoCal.get(Calendar.YEAR)- fechaNacimientoCal.get(Calendar.YEAR);
        int mes = fechaSucesoCal.get(Calendar.MONTH)- fechaNacimientoCal.get(Calendar.MONTH);
        int dia = fechaSucesoCal.get(Calendar.DATE)- fechaNacimientoCal.get(Calendar.DATE);
        //Se ajusta el año dependiendo el mes y el día
        if (mes < 0 || ( mes == 0 && dia < 0) ){
            anio--;
        }
        return anio;
    }
    
    /* clase necesaria para contar los diagnosticos */
    class ContadorMedicamentos {
        
        List<Registro> listaRegistros;
        
        public ContadorMedicamentos() {
            listaRegistros = new ArrayList<>();
        }
        
        public void aparecioMedicamento(Monodroga medicamento) {
            Registro registro;
            if (medicamento == null ) {
                return;
            }
            registro = new Registro(medicamento);
            if (listaRegistros.contains(registro)) {
                registro = listaRegistros.get(listaRegistros.indexOf(registro));
                registro.aparecio();
            } else {
                registro = new Registro(medicamento);
                listaRegistros.add(registro);
            }
        }
        
        public List<Monodroga> getListaOrdenadaMayorAmenor() {
            Collections.sort(listaRegistros);
            List<Monodroga> listaDiagnosticoOrdenada = new ArrayList<>();
            for (Registro registro : listaRegistros) {
                listaDiagnosticoOrdenada.add(registro.getMedicamento());
                registro.getMedicamento().setCantidad(registro.getCantidad());
            }
            return listaDiagnosticoOrdenada;
        }
        
    }
    
    /* clase necesaria para contar los diagnosticos */
    class Registro implements Comparable<Registro> {
        
        private Monodroga medicamento;
        private Long cantidad;
        
        public Registro(Monodroga medicamento) {
            this.medicamento = medicamento;
            cantidad = 1L;
        }
        
        public void aparecio() {
            cantidad++;
        }
        
        public Long getCantidad() {
            return cantidad;
        }
        
        public Monodroga getMedicamento() {
            return medicamento;
        }
        
        @Override
        public boolean equals(Object otro) {
            Registro otroRegistro = (Registro) otro;
            return this.medicamento.getId().equals(otroRegistro.getMedicamento().getId());
        }

        @Override
        public int hashCode() {
            return medicamento.hashCode();
        }

        @Override
        public int compareTo(Registro t) {
            if (this.cantidad < t.getCantidad()) {
                return 1;
            }
            return -1;
        }
    }
}
